package com.fduss.mazegame.client.gui.fxgui;

import com.fduss.mazegame.CLOptions;
import com.fduss.mazegame.client.Localizer;
import com.fduss.mazegame.client.MsgToUsr;
import com.fduss.mazegame.core.Update;
import com.fduss.mazegame.core.unit.Hero;
import com.fduss.mazegame.core.unit.feature.Icon;
import com.fduss.mazegame.server.ClientView;
import com.fduss.mazegame.server.Server;
import com.fduss.mazegame.util.Direction;
import com.fduss.mazegame.util.Queue;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.MenuBar;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

/**
 * a common java fx gui
 *
 * @Author duocai wu
 * @Date 2017/9/28
 * @Time 20:20
 */
abstract class FxGui extends Application implements Runnable {

    protected static Server server;
    protected static Localizer localizer;
    protected static CLOptions opts;
    protected static Theme theme;
    static double TILE_SIZE = 20;
    protected GridPane map;
    private Stage stage;
    private int delay;

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setTitle("maze game");
        this.stage = primaryStage;
        server = new Server(opts.getServerSpec()).init();
        localizer = new Localizer(opts.getLanguage());

        // prepare resources
        this.delay = 10;
        initResources();
        initGameFrame();
        addKeyControls();
        createAndShowGui();

        // start game
        new Thread(this).start();
    }

    @Override
    public void run() {
        while (true) {
            showUpdates(server.getUpdates());
            triggerHeroDied(!server.isHeroAlive());
            triggerGameOver(server.isGameOver());
            if (!server.isHeroAlive() || server.isGameOver())
                break;
            try {
                Thread.sleep(this.delay);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void changeTheme(Theme theme) {
        FxGui.theme = theme;
        initResources();
        updateMap(server.getClientView().getGameView());
    }

    protected abstract void initResources();

    /**
     * update the total maze
     *
     * @param icons
     */
    protected abstract void updateMap(Icon[][] icons);

    /**
     * modify the maze locally
     *
     * @param updates
     */
    protected abstract void showUpdates(Queue<Update> updates);

    private void initGameFrame() {
        VBox frame = new VBox();
        MenuBar menuBar = new MyMenuBar(localizer, this);
        map = new GridPane();
        frame.getChildren().addAll(menuBar, map);
        this.stage.setScene(new Scene(frame));
    }

    private void addKeyControls() {
        map.setOnKeyPressed(e -> {
            Direction d = null;
            boolean undoBool = false;
            switch (e.getCode()) {
                case W:
                    d = Direction.NORTH;
                    break;
                case A:
                    d = Direction.WEST;
                    break;
                case S:
                    d = Direction.SOUTH;
                    break;
                case D:
                    d = Direction.EAST;
                    break;
                case B:
                    undoBool = true;
                    break;
                case K:
                    server.killMonster();
                    return;
                case P:
                    server.pickTreasure();
                    return;
                case C:
                    showScore(server.checkScore());
                    return;
            }
            if (d != null || undoBool)
                movePlayer(d, undoBool);
        });
    }

    private void createAndShowGui() {
        ClientView view = server.getClientView();
        updateMap(view.getGameView());

        stage.show();
        map.requestFocus(); // request focus
    }

    private void movePlayer(Direction d, boolean undo) {
        if (undo)
            server.undo();
        else
            server.moveHero(d);
    }

    private void showScore(Hero hero) {
        Alert alert = new Alert(Alert.AlertType.INFORMATION);
        alert.setTitle("Information");
        alert.setHeaderText("Information");
        alert.setContentText(String.format(
                localizer.get(MsgToUsr.SCORE),
                hero.getSteps(),
                hero.getKilling(),
                hero.getPicking()
        ));
        alert.showAndWait();
    }

    private void triggerHeroDied(boolean died) {
        if (died) {
            Platform.runLater(() -> {
                Alert alert = new Alert(Alert.AlertType.INFORMATION);
                alert.setTitle("Information");
                alert.setHeaderText("Information");
                alert.setContentText(localizer.get(MsgToUsr.HERO_DIED));
                alert.showAndWait();
                triggerGameOver(true);
            });
        }
    }

    private void triggerGameOver(boolean gameOver) {
        if (!gameOver) {
            return;
        }
        System.out.println(localizer.get(MsgToUsr.PASS_ALL_LEVEL));
        System.exit(0);
    }
}
